
asynx.namespace("datalist");

asynx.datalist = function() {
	this._init(arguments);
};

asynx.datalist.prototype = {
	
	_init: function(args) {

		this.UID = "asynx_" + asynx.uid();
		
		this.attributes = args[0];
		
		this.onMouseOver = null;

		this.onMouseOut = null;
		
		this.onMouseDown = null;

		this.onMouseUp = null;
		
		this.onClick = null;
	
		this.onDblClick = null;
		
		this.table = null;
		
		this.container = asynx(this.attributes.container);
		
		this.selectedElement = null;
		
		this.XPATH_CONTAINER = "/div[@asynx_dl='{uid}']".replace("{uid}", this.UID);
		
		this.XPATH_TABLE = this.XPATH_CONTAINER + "/table[@dl_table]";
		
		this.XPATH_ROWS = this.XPATH_TABLE + "//tr[@dl_row]";
		
		this.XPATH_COLUMNS = this.XPATH_TABLE + "//td[@dl_column]";
		
		this.XPATH_ICONS = this.XPATH_TABLE + "//img[@dl_icon]";
		
		if (this.attributes.data instanceof asynx.datalist.DataSource) {
			
			this.DS = this.attributes.data;
		
		}else{
			
			this.DS = new asynx.datalist.DataSourceJSON(this.attributes.data);
			this.DS.filterByFields = [ "text", "value" ];
			
		}
		
		this._initDefaultCss();
		this._initContainer();
	},
	
	_initContainer: function() {
		
		asynx.dom.setAttribute(this.container, "asynx_dl", this.UID);
		
		this.setContainerCss(this.containerCss);
		
	},
	
	create: function() {
		this.doQuery("");
	},
	
	show: function() {
		asynx.css(this.container, { visibility: 'visible' });
	},
	
	hide: function() {
		asynx.css(this.container, { visibility: 'hidden' });
	},
	
	flushCache: function() {
		this.DS.flushCache();
	},
	
	doQuery: function(query) {
		
		var scope = this, highlight = scope.attributes.highlight,
			fastRender = scope.attributes.fastRender, fn = arguments[1],fnArgs = arguments[2];
		
		scope.selectedElement = null;
		
		return this.DS.doQuery(query, function(f, o) {
			
			// populate lines
			scope._populateList(f);
			
			asynx(scope.XPATH_TABLE).css({
				border: '0px',
				tableLayout: 'fixed',
				width: '100%'
			});
			
			scope.setContainerCss();
			scope.setTableCss();
			scope.setHighlightColor(scope.highlightColor);
			
			var doAfterRender = function() {
				
				asynx(scope.XPATH_ROWS).each(function(row) {
					row._asynx_ac = scope;
					
					row.onmouseover = function(e) {
						if (highlight == "row") scope._colorize(row);
					};
					
					row.className = scope.rowCss;
				});
				
				asynx(scope.XPATH_COLUMNS).each(function(c) {
					c._asynx_ac = scope;
					
					scope._bindUserEvents(c);
					
					if (scope.attributes.gridLayout) {
						var style = c.style;
						style.whiteSpace = 'nowrap';
						style.overflow = 'hidden';
						style.emptyCells = 'show';
					}
					
					c.className = scope.columnCss;
				});
				
			};
			
			if (fastRender == true || asynx.isUndef(fastRender)) {
			
				asynx.delay(function() {
					doAfterRender();
				}, 1);
			
			}else{
			
				doAfterRender();
			
			}
			
			// user callback
			if (asynx.isFunction(fn)) fn.apply(scope, [fnArgs]);
		});
	},
	
	_populateList: function(f) {
		
		var scope = this, highlight = scope.attributes.highlight;
		
		// better performance then DOM
		var buffer = new asynx.StringBuffer();
		
		
		buffer.append("<table cellpading=0 cellspacing=0 dl_table=1>");
		
		asynx.each(f, function(rowGrid, x) {

			buffer.append("<tr dl_row=");
			buffer.append(x);
			buffer.append(">");
			
				asynx.each(rowGrid, function(column) {
					
					buffer.append("<td dl_column=1 dl_value='");
					buffer.append(column.value);
					buffer.append("'>");
					
					if (column.icon) {
						buffer.append("<img dl_icon=1 src='");
						buffer.append(column.icon);
						buffer.append("' ");
						buffer.append(" class='");
						buffer.append(scope.iconCss);
						buffer.append("'>");
					}
					
					buffer.append(column.text);
					buffer.append("</td>");
					
				}, this);
			
			buffer.append("</tr>");
			
		}, this);
		
		buffer.append("</table>");
		
		scope.container.innerHTML = buffer.toString();
		delete buffer;
	},
	
	
	_bindUserEvents: function(element) {
		
		var scope = this;
		
		var info = {
			td: element,
			tr: element.parentNode,
			innerHTML: element.innerHTML,
			text: element.innerHTML,
			value: element.getAttribute('dl_value')
		};
		
		
		element.onmouseover = function(e) {
			e = asynx.event.fix(e?e:event);

			if (scope.attributes.highlight == "column")
				scope._colorize(element);
	
			if (scope.onMouseOver)
				scope.onMouseOver.apply(scope, [info, e ]);
		};
		
		if (scope.onMouseOut)
			element.onmouseout = function(e) {
				e = asynx.event.fix(e?e:event);
				scope.onMouseOut.apply(scope, [info, e ]);
			};
		
		if (scope.onMouseDown)
			element.onmousedown = function(e) {
				e = asynx.event.fix(e?e:event);
				scope.onMouseDown.apply(scope, [info, e]);
			};
				
		if (scope.onMouseUp)
			element.onmouseup = function(e) {
				e = asynx.event.fix(e?e:event);
				scope.onMouseUp.apply(scope, [info, e]);
			};
	
		if (scope.onClick)
			element.onclick = function(e) {
				e = asynx.event.fix(e?e:event);
				scope.onClick.apply(scope, [info, e]);
			};
				
		if (scope.onDblClick)
			element.ondblclick = function(e) {
				e = asynx.event.fix(e?e:event);
				scope.onDblClick.apply(scope, [info, e]);
			};
			
	},
	
	_colorize: function(el) {
		el = asynx(el);
		
		if (this.selectedElement)
			asynx(this.selectedElement).css({bg:""});

		el.css({ bg: this.highlightColor });
		this.selectedElement = el;
	},
	
	
	/**
	 * DataList CSS
	 */
	
	_initDefaultCss: function() {
		
		this.highlightColor = "#C3D9FF";
		this.rowCss = null;
		this.columnCss = null;
		this.iconCss = null;
		this.tableCss = {};
		
		this.containerCss = {
			bg:"#E8EEF7",
			border:"#C3D9FF 1px solid"
		};
	},
	
	setHighlightColor: function(color) {
	
		this.highlightColor = color;
		if (this.selectedItem)	this.selectedItem.css({bg:this.highlightColor});
	
	},
	
	setRowCss: function(css) {
		this.rowCss = css;
	},
	
	setColumnCss: function(css) {
		this.columnCss = css;
	},
	
	setIconCss: function(css) {
		this.iconCss = css;
	},
	
	setContainerCss: function(css) {

		this.containerCss =  asynx.extend(this.containerCss, css);
		if (this.container) this.container.css(this.containerCss);		

	},
	
	setTableCss: function(css) {
		
		this.tableCss = asynx.extend(this.tableCss, css);

		asynx(this.XPATH_TABLE).each(function(t) {
			asynx(t).css(this.tableCss);
		},this);	

	}
	
};


/**
 * DataSource
 */
asynx.datalist.DataSource = function() { /* abstract class */ };

asynx.datalist.DataSource.prototype._init = function() {
	
	this.jsonOutputData = null;

	this.numRows = 0;
	
	this.cache = true;
	
	this.cached = {};
	
	this.maxResults = 100;
	
	this.filterByFields = ["text", "value"];
	
};

asynx.datalist.DataSource.prototype.doQuery = function(query, fn) {
	/* overwrite this */
};
	
asynx.datalist.DataSource.prototype.flushCache = function() {
	this.cached = {};
};


/**
 * DataSource for JSON Input data
 * @extends DataSource
 */
asynx.datalist.DataSourceJSON = function() {
	this.inputData = arguments[0];
	
	// initialize DataSource public variables
	this._init();
};

asynx.datalist.DataSourceJSON.prototype = new asynx.datalist.DataSource();

asynx.datalist.DataSourceJSON.prototype.doQuery = function(query, fn) {
	
	if (!this.cached[query]) {
		
		var	tResults = 0,
			filtered = [],
			regex = new RegExp(query, "gi");
		
		asynx.each(this.inputData, function(rowGrid) {
				
			tResults++;
			var canAddRow = false;
			
			asynx.each(rowGrid, function(column) {
				
				asynx.each(this.filterByFields, function(field) {
					
					var columnValue = column[field];
					
					if (!asynx.isString(columnValue)) return null;
					
					if (regex.test(columnValue)) canAddRow = true;

				}, this);
	
			}, this);
			
			if (canAddRow && tResults<=this.maxResults)
				filtered.push(rowGrid);
			
		}, this);
		
		this.jsonOutputData = filtered;
		
		if (this.cache)
			this.cached[query] = this.jsonOutputData;
		
	}
	else{
		this.jsonOutputData = this.cached[query];
	}
	
	this.numRows = this.jsonOutputData.length;
	
	if (asynx.isFunction(fn))
		fn.apply(this, [this.jsonOutputData, this.inputData]);
	
	return filtered;	
};


/**
 * DataSourceXML
 * @extends DataSource
 */

asynx.datalist.DataSourceXML = function() {
	this.inputData = arguments[0];
	
	// initialize DataSource public variables
	this._init();
};

asynx.datalist.DataSourceXML.prototype = new asynx.datalist.DataSource();

asynx.datalist.DataSourceXML.prototype.doQuery = function(query, fn) {
	
	var xml = this.inputData, filtered = [];
	
	asynx("/datalist//row//column", xml).each(function(columns, x) {
		
		
		
		var text = columns.getElementsByTagName("text")[0].firstChild.nodeValue;
		var value = columns.getElementsByTagName("value")[0].firstChild.nodeValue;
		var icon = columns.getElementsByTagName("icon")[0].firstChild.nodeValue;
		
		
		log(text  + ":"  + value  +":" + icon);
			
		
	});
	
};

/**
 * DataSourceAJAX
 * @extends DataSource
 */
asynx.datalist.DataSourceAJAX = function(url, type) {
	this.url = url;
	this.urlData = arguments[2]||{};
	this.type = type||"json";
	this.XHR = null;
	this.DS = null;
	
	// initialize DataSource public variables
	this._init();
};

asynx.datalist.DataSourceAJAX.prototype = new asynx.datalist.DataSource();

asynx.datalist.DataSourceAJAX.prototype.flushCache = function() {
	this.cached = {};
	this.DS.flushCache();
};

asynx.datalist.DataSourceAJAX.prototype.doQuery = function(query, fn) {
	
	var scope = this;
	
	this.urlData = asynx.extend(this.urlData, {query:query});
	
	if (this.DS && this.DS.cache && this.DS.cached[query]) {
		
		// if cached
		this.DS.doQuery(query, fn);
		this.inputData = this.DS.inputData;
		this.jsonOutputData = this.DS.cached[query];
		this.numRows = this.DS.numRows;
	}else{
		
		if (this.XHR) {
			// aborting unfinished XHR transaction
			this.XHR.xmlhttp.abort();
		}

		if (this.type == "json") {
			
			scope.XHR = asynx.$getJSON(this.url, this.urlData, function(json,a) {
				// singleton DS
				scope.DS = scope.DS || new asynx.datalist.DataSourceJSON();
				scope.DS.cache = scope.cache;
				scope.DS.maxResults = scope.maxResults;
				scope.DS.filterByFields = scope.filterByFields;
				scope.DS.inputData = json;
				scope.DS.doQuery(query, fn);
				// coping DS Ajax values to DS JSON values
				scope.inputData = scope.DS.inputData;
				scope.numRows = scope.DS.numRows;
				scope.jsonOutputData = json;
			});
		
		}
		
		if (this.type == "xml") {
			
			scope.XHR = asynx.$getXML(this.url, this.urlData, function(xml,a) {
				// singleton DS
				scope.DS = scope.DS || new asynx.datalist.DataSourceXML();
				scope.DS.cache = scope.cache;
				scope.DS.maxResults = scope.maxResults;
				scope.DS.filterByFields = scope.filterByFields;
				scope.DS.inputData = xml;
				scope.DS.doQuery(query, fn);
			});
			
		}
		
		if (this.type == "array") {
			/* TODO */
		}
		
	}
};


